package nobodyCodesThat;

import java.io.IOException;

/**
 * Dumps data in hexadecimal format.
 * <p>
 * Provides a single function to take an array of bytes and display it in
 * hexadecimal form.
 * <p>
 * Origin of code: POI.
 * 
 * @author Scott Sanders
 * @author Marc Johnson
 * @version $Id: HexDump.java 369075 2006-01-14 18:23:42Z scolebourne $
 */
public class HexDump {

	/**
	 * Instances should NOT be constructed in standard programming.
	 */
	public HexDump() {
		super();
	}

	/**
	 * Dump an array of bytes to an OutputStream.
	 * 
	 * @param data
	 *            the byte array to be dumped
	 * @param offset
	 *            its offset, whatever that might mean
	 * @param stream
	 *            the OutputStream to which the data is to be written
	 * @param index
	 *            initial index into the byte array
	 * 
	 * @throws IOException
	 *             is thrown if anything goes wrong writing the data to stream
	 * @throws ArrayIndexOutOfBoundsException
	 *             if the index is outside the data array's bounds
	 * @throws IllegalArgumentException
	 *             if the output stream is null
	 */

	public static String dump(byte[] data, long offset, int index)
			throws IOException, ArrayIndexOutOfBoundsException,
			IllegalArgumentException {

		if ((index < 0) || (index >= data.length)) {
			throw new ArrayIndexOutOfBoundsException("illegal index: " + index
					+ " into array of length " + data.length);
		}

		long display_offset = offset + index;
		StringBuffer buffer = new StringBuffer(74);
		StringBuffer fillThis = new StringBuffer();
		for (int j = index; j < data.length; j += 16) {
			int chars_read = data.length - j;

			if (chars_read > 16) {
				chars_read = 16;
			}
			buffer.append(dump(display_offset)).append(' ');
			for (int k = 0; k < 16; k++) {
				if (k < chars_read) {
					buffer.append(dump(data[k + j]));
				} else {
					buffer.append("  ");
				}
				buffer.append(' ');
			}
			for (int k = 0; k < chars_read; k++) {
				if ((data[k + j] >= ' ') && (data[k + j] < 127)) {
					buffer.append((char) data[k + j]);
				} else {
					buffer.append('.');
				}
			}
			buffer.append(EOL);
			fillThis.append(buffer.toString());
			buffer.setLength(0);
			display_offset += chars_read;
		}
		return fillThis.toString();
	}

	/**
	 * The line-separator (initializes to "line.separator" system property.
	 */
	public static final String EOL = System.getProperty("line.separator");

	private static final StringBuffer _lbuffer = new StringBuffer(8);

	private static final StringBuffer _cbuffer = new StringBuffer(2);

	private static final char[] _hexcodes = { '0', '1', '2', '3', '4', '5',
			'6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

	private static final int[] _shifts = { 28, 24, 20, 16, 12, 8, 4, 0 };

	/**
	 * Dump a long value into a StringBuffer.
	 * 
	 * @param value
	 *            the long value to be dumped
	 * @return StringBuffer containing the dumped value.
	 */
	private static StringBuffer dump(long value) {
		_lbuffer.setLength(0);
		for (int j = 0; j < 8; j++) {
			_lbuffer.append(_hexcodes[((int) (value >> _shifts[j])) & 15]);
		}
		return _lbuffer;
	}

	/**
	 * Dump a byte value into a StringBuffer.
	 * 
	 * @param value
	 *            the byte value to be dumped
	 * @return StringBuffer containing the dumped value.
	 */
	private static StringBuffer dump(byte value) {
		_cbuffer.setLength(0);
		for (int j = 0; j < 2; j++) {
			_cbuffer.append(_hexcodes[(value >> _shifts[j + 6]) & 15]);
		}
		return _cbuffer;
	}

	// ////////////////////////////////////////////////////////
	private static char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
			'9', 'a', 'b', 'c', 'd', 'e', 'f' };

	public static String toHexAsciiString(byte[] b, int lfAtPos) {
		if (b == null || b.length == 0)
			return "";
		StringBuffer sb = new StringBuffer(4 * b.length);
		int i, r;
		for (i = 0; i < b.length; i++) {
			sb.append(hex[(b[i] >>> 4) & 0xF]).append(hex[b[i] & 0xF]).append(
					' ');
			if ((i + 1) % lfAtPos == 0) {
				for (int j = i - lfAtPos + 1; j <= i; j++) {
					if (32 <= b[j] && b[j] <= 126)
						sb.append((char) b[j]);
					else
						sb.append('.');
				}
				sb.append('\n');
			}
		}
		if (0 < (r = b.length % lfAtPos)) {
			for (i = 0; i < (lfAtPos - r) * 3; i++)
				sb.append(' ');
			for (i = b.length - r; i < b.length; i++) {
				if (32 <= b[i] && b[i] <= 126)
					sb.append((char) b[i]);
				else
					sb.append('.');
			}
		} else
			sb.deleteCharAt(sb.length() - 1);
		return sb.toString();
	}
}
